#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <poll.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <libdrm/drm.h>
#include <libdrm/drm_mode.h>
#include <linux/videodev2.h>
#include <linux/v4l2-subdev.h>

#include <xf86drm.h>
#include <xf86drmMode.h>

struct setup {
	char module[32];
    char video[32];
    char modestr[32];
	uint32_t crtc_id;
    uint32_t conn_id;
	int crtcIdx,planeIdx,connIdx;
	uint32_t w;
    uint32_t h;
    uint32_t bpp;
    uint32_t pitch;
    unsigned int use_wh : 1;
};

static void usage(char *name)
{
	fprintf(stderr, "usage: %s [-Moish]\n", name);
	fprintf(stderr, "\t-M <drm-module>\tset DRM module\n");
	fprintf(stderr, "\t-o <connector_id>:<crtc_id>:<mode>\tset a mode\n");
	fprintf(stderr, "\t-i <video-node>\tset video node like /dev/video*\n");
	fprintf(stderr, "\t-S <width,height>\tset input resolution\n");
	fprintf(stderr, "\t-h\tshow this help\n");
	fprintf(stderr, "\n\tDefault is to dump all info.\n");
}

void cap2s(__u32 capabilities)
{
    if (capabilities & V4L2_CAP_VIDEO_CAPTURE)
        printf("        Video Capture\n");
    if (capabilities & V4L2_CAP_VIDEO_OUTPUT)
        printf("        Video Output\n");
    if (capabilities & V4L2_CAP_VIDEO_OVERLAY)
        printf("        Video Overly\n");
    if (capabilities & V4L2_CAP_VBI_CAPTURE)
        printf("        VBI Capture\n");
    if (capabilities & V4L2_CAP_VBI_OUTPUT)
        printf("        VBI Output\n");
    if (capabilities & V4L2_CAP_SLICED_VBI_CAPTURE)
        printf("        Sliced VBI Capture\n");
    if (capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
        printf("        Sliced VBI Output\n");
    if (capabilities & V4L2_CAP_RDS_CAPTURE)
        printf("        RDS Capture\n");
    if (capabilities & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)
        printf("        Video Output Overlay\n");
    if (capabilities & V4L2_CAP_HW_FREQ_SEEK)
        printf("        HW Frequency Seek\n");
    if (capabilities & V4L2_CAP_RDS_OUTPUT)
        printf("        RDS Output\n");

    if (capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)
        printf("        Video Capture Mplane\n");
    if (capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)
        printf("        Video Output Mplane\n");
    if (capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
        printf("        M2M Mplane Device\n");
    if (capabilities & V4L2_CAP_VIDEO_M2M)
        printf("        M2M Device\n");
    if (capabilities & V4L2_CAP_TUNER)
        printf("        Tuner\n");
    if (capabilities & V4L2_CAP_AUDIO)
        printf("        Audio\n");
    if (capabilities & V4L2_CAP_RADIO)
        printf("        Radio\n");
    if (capabilities & V4L2_CAP_MODULATOR)
        printf("        Modulator\n");

    if (capabilities & V4L2_CAP_SDR_CAPTURE)
        printf("        SDR Capture\n");
    if (capabilities & V4L2_CAP_EXT_PIX_FORMAT)
        printf("        Extended Pix Format\n");
    if (capabilities & V4L2_CAP_SDR_OUTPUT)
        printf("        SDR Output\n");
    if (capabilities & V4L2_CAP_META_CAPTURE)
        printf("        Metadata Capture\n");
    if (capabilities & V4L2_CAP_READWRITE)
        printf("        Read / Write\n");
    if (capabilities & V4L2_CAP_ASYNCIO)
        printf("        Async I/O\n");
    if (capabilities & V4L2_CAP_STREAMING)
        printf("        Streaming\n");
    if (capabilities & V4L2_CAP_META_OUTPUT)
        printf("        Metadata Output\n");
    if (capabilities & V4L2_CAP_TOUCH)
        printf("        Touch Device\n");
    if (capabilities & V4L2_CAP_IO_MC)
        printf("        I/O Media Controller\n");
    if (capabilities & V4L2_CAP_DEVICE_CAPS)
        printf("        Device Capabilities\n");
    printf("\n");
}

void get_capabilities(struct v4l2_capability cap)
{
    printf("------- VIDIOC_QUERYCAP ----\n");
    printf("  driver: %s\n", cap.driver);
    printf("  card: %s\n", cap.card);
    printf("  bus_info: %s\n", cap.bus_info);
    printf("  version: %d.%d.%d\n",
           (cap.version >> 16) & 0xff,
           (cap.version >> 8) & 0xff,
           (cap.version & 0xff));
    printf("  capabilities: %08X\n", cap.capabilities);
    cap2s(cap.capabilities);

    if (cap.capabilities & V4L2_CAP_DEVICE_CAPS) {
		printf("\tDevice Caps      : 0x%08x\n", cap.device_caps);
        cap2s(cap.device_caps);
    }

    return;
}

static int parse_args(int argc, char *argv[], struct setup *s)
{
	if (argc <= 1)
		usage(argv[0]);

	int c, ret;
	memset(s, 0, sizeof(*s));

	while ((c = getopt(argc, argv, "M:o:i:S:h")) != -1) {
		switch (c) {
		case 'M':
			strncpy(s->module, optarg, 31);
			break;
		case 'o':
			ret = sscanf(optarg, "%u:%u:%31s", &s->conn_id, &s->crtc_id,
				s->modestr);
			if (ret != 3){
                printf( "incorrect mode description\n");
				return -1;
            }
			break;
		case 'i':
			strncpy(s->video, optarg, 31);
			break;
		case 'S':
			ret = sscanf(optarg, "%u,%u", &s->w, &s->h);
			if (ret != 2){
                printf( "incorrect input size\n");
				return -1;
            }
            s->use_wh = 1;
			break;
		case '?':
		case 'h':
			usage(argv[0]);
			return -1;
		}
	}

	return 0;
}


int main(int argc, char *argv[])
{
    int ret = 0;
    int drmfd;
    int v4lfd;
    uint32_t conn_id,crtc_id;

    struct setup s;
	drmModeRes *res;
	drmModeConnector *conn;
	drmModePlaneRes *plane_res;


    ret = parse_args(argc, argv, &s);
    if (ret < 0)
    {
        printf("parse args failed\r\n");
        return -1;
    }
    if (s.module[0] == 0){
        printf("DRM module is missing\r\n");
        return -1;
    }
    if (s.video[0] == 0){
        printf("V4L2 module is missing\r\n");
        return -1;
    }
    /*open DRM driver:virtio_gpu*/
    drmfd = drmOpen(s.module, NULL);
    if (drmfd < 0){
        printf("drmOpen(%s) failed\n", s.module);
    }
    /*open V4L2 Driver:/dev/video**/
    v4lfd = open(s.video, O_RDWR);
    if (v4lfd < 0){
        printf("open(%s) failed\n", s.video);
    }
    /*VIDIOC_QUERYCAP*/
	struct v4l2_capability caps;
	memset(&caps, 0, sizeof(caps));
    ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &caps);
    if (ret < 0){
        printf("[video]:VIDIOC_QUERYCAP failed\r\n");
    }
    get_capabilities(caps);
    if (~caps.capabilities & V4L2_CAP_VIDEO_CAPTURE){
        printf("[video]:single plane is not support\r\n");
    }
    /*VIDIOC_G_FMT*/
	struct v4l2_format fmt;
	memset(&fmt, 0, sizeof fmt);
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(v4lfd, VIDIOC_G_FMT, &fmt);
    if (ret < 0){
        printf("[video]:VIDIOC_G_FMT failed\r\n");
    }

	printf("G_FMT(start): width = %u, height = %u, size = %u, pitch = %u ,4cc = %.4s\n",
		fmt.fmt.pix.width,
		fmt.fmt.pix.height,
		fmt.fmt.pix.sizeimage,
		fmt.fmt.pix.bytesperline,
		(char*)&fmt.fmt.pix.pixelformat);

	if (s.use_wh) {
		fmt.fmt.pix.width = s.w;
		fmt.fmt.pix.height = s.h;
		fmt.fmt.pix.bytesperline = 4*s.w;
		printf("use w=%d,h=%d,pitch=%d\r\n",
						fmt.fmt.pix.width,
						fmt.fmt.pix.height,
						fmt.fmt.pix.bytesperline);
	}
	ret = ioctl(v4lfd, VIDIOC_S_FMT, &fmt);
    if (ret < 0){
        printf("[video]:VIDIOC_S_FMT failed\r\n");
    }

	ret = ioctl(v4lfd, VIDIOC_G_FMT, &fmt);
    if (ret < 0){
        printf("[video]:VIDIOC_G_FMT failed\r\n");
    }
	printf("G_FMT(final): width = %u, height = %u, size = %u, pitch = %u ,4cc = %.4s\n",
		fmt.fmt.pix.width,
		fmt.fmt.pix.height,
		fmt.fmt.pix.sizeimage,
		fmt.fmt.pix.bytesperline,
		(char*)&fmt.fmt.pix.pixelformat);

	struct v4l2_requestbuffers rqbufs;
	memset(&rqbufs, 0, sizeof(rqbufs));
	rqbufs.count = 2;
	rqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	rqbufs.memory = V4L2_MEMORY_DMABUF;

	ret = ioctl(v4lfd, VIDIOC_REQBUFS, &rqbufs);

	printf("VIDIOC_REQBUFS succeed!\n\n");

    return 0;
}